<!--
 * @Description: 拓扑监控
 * @Author: 0001100986/LiuShuai
 * @Date: 2021-06-10 14:10:58
 * @LastEditTime: 2021-11-09 17:55:05
 * @LastEditors: 0001100986/LiuShuai
-->
<template>
  <div class="page-content">
    <el-form label-width="80px" label-position="right">
      <el-form-item label="租户选择">
        <el-select
          class="order-cover-select"
          v-model="tenant"
          clearable
          @clear="filterVmModel"
          placeholder="请选择租户"
          filterable
          v-el-select-loadmore="loadmore"
          :filter-method="filterVmModel"
          @change="handleSelect"
        >
          <el-option
            v-for="item in options"
            :key="item.value"
            :label="item.label"
            :value="item.value"
          ></el-option>
        </el-select>
      </el-form-item>
    </el-form>
    <div class="topology-box" id="container"></div>
    <el-dialog
      title="告警信息"
      :visible.sync="dialogAlarms"
      :close-on-click-modal="false"
      width="1100px"
      @close="cancel"
    >
      <alarm-table :ne="ne" ref="alarms" v-if="dialogAlarms" />
    </el-dialog>
  </div>
</template>
<script>
import G6 from '@antv/g6';
import alarmTable from '@/pages/configure/alarms';
export default {
  name: 'topology',
  components: {
    alarmTable
  },
  directives: {
    // 计算是否滚动到最下面
    'el-select-loadmore': {
      bind (el, binding) {
        // 获取element-ui定义好的scroll盒子
        const SELECTWRAP_DOM = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap');
        SELECTWRAP_DOM.addEventListener('scroll', function() {
          // scrollHeight 获取元素内容高度(只读)
          // scrollTop 获取或者设置元素的偏移值,常用于, 计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
          // clientHeight 读取元素的可见高度(只读)
          // 如果元素滚动到底, 下面等式返回true, 没有则返回false:
          const condition = Math.round(this.scrollHeight - this.scrollTop) <= this.clientHeight;
          if (condition) {
            binding.value();
          }
        });
      }
    }
  },
  data () {
    return {
      // 告警数据
      dialogAlarms: false,
      ne: {},
      // Select 选择的数据
      tenant: '',
      tenantId: null,
      // 拓扑数据
      treeData: {
        nodes: [],
        edges: []
      },
      // G6对象
      devicetopologyChart: null,
      // 分页数据
      options: [],
      // 分页参数
      page: {
        page: 1,
        pageSize: 15,
        total: 0
      },
      // 筛选文本
      filterText: ''
    };
  },
  mounted () {
    let _this = this;
    _this.$nextTick(() => {
      _this.loadmore(true);
    });
  },
  methods: {
    // 分页方法 详见 directives
    loadmore (firstTag) {
      let _this = this;
      // 筛选时下拉不触发分页
      if (!_this.filterText) {
        if (!firstTag) {
          _this.page.page++;
        }
        let params = {
          limit: _this.page.pageSize,
          offset: (_this.page.page - 1) * _this.page.pageSize
        };
        _this.tenantQuery(params);
      }
    },
    // 筛选方法
    filterVmModel (value) {
      let _this = this;
      _this.filterText = value;
      // 筛选数据，有值时清空选项并按照关键字搜索对应结果集合
      if (value) {
        _this.options = [];
        _this.$nextTick(() => {
          _this.page = {
            page: 1,
            pageSize: 15,
            total: 0
          };
          let params = {
            label: _this.filterText,
            limit: _this.page.pageSize,
            offset: (_this.page.page - 1) * _this.page.pageSize
          };
          _this.tenantQuery(params);
        });
      } else {
        // 空值时，触发重置
        this.options = [];
        this.$nextTick(() => {
          _this.page = {
            page: 1,
            pageSize: 15,
            total: 0
          };
          let params = {
            limit: _this.page.pageSize,
            offset: (_this.page.page - 1) * _this.page.pageSize
          };
          _this.tenantQuery(params);
        });
      }
    },
    // 请求租户
    tenantQuery (data) {
      let _this = this;
      _this.$http.post({
        url: '/api/sdwan/tenant/getMultiDg',
        data
      }).then(res => {
        let { status, resultBody } = res;
        if (status === '0') {
          let { objects } = resultBody;
          let arry = [];
          for (let i = 0; i < objects.length; i++) {
            arry.push({
              label: objects[i].userlabel,
              value: objects[i].id
            });
          }
          _this.options.push(...arry);
        }
      });
    },
    // 过滤SN
    filterSN (val) {
      if (val === '' || val === null || val === undefined) {
        return '无';
      } else {
        // 转换数组
        let mac = val.split(':');
        for (let i = 0; i < mac.length; i++) {
          if (i === 0) {
            if (mac[i].length === 2) {
              mac[i] = mac[i];
            } else {
              mac[i] = '0' + mac[i];
            }
          } else {
            if (mac[i].indexOf(' ') === 0) {
              mac[i] = '0' + mac[i].substring(1, mac[i].length);
            }
          }
        }
        mac = mac.join(':');
        return mac;
      }
    },
    // 过滤状态
    filterStatus (val) {
      if (val.managementState === 'ONLINE') {
        if (val.connectionState === 'CONNECTED') {
          return '在线';
        }
        if (val.connectionState === 'DISCONNECTED') {
          return '断链';
        }
      }
      if (val.connectionState === 'UNKNOWN') {
        return '未知';
      }
    },
    // 数组去重
    uniqueArr (arr) {
      if (arr.length === 0) {
        return;
      }
      for (let i = 0; i < arr.length; i++) {
        for (let j = i + 1; j < arr.length; j++) {
          if (arr[i].id === arr[j].id) {
            arr.splice(j, 1);
            j--;
          }
        }
      }
      return arr;
    },
    // 选择查询
    handleSelect (val) {
      let _this = this;
      if (val) {
        _this.tenant = val ? _this.options.find(ele => ele.value === val).label : '';
        _this.tenantId = val;
        _this.tenantTunnel();
      } else {
        document.getElementById('container').style.display = 'none';
      }
    },
    // 点选刷新
    tenantTunnel () {
      let _this = this;
      if (_this.tenantId !== null || _this.tenantId !== '' || _this.tenantId !== undefined) {
        _this.$http.get({
          url: '/api/sdwan/businesslayout/query/chart/' + _this.tenantId
        }).then(res => {
          let { status, resultBody } = res;
          if (status === '0') {
            if (resultBody.length === 0) {
              _this.$message.warning('该租户下暂无数据！');
              document.getElementById('container').style.display = 'none';
            } else {
              document.getElementById('container').style.display = 'block';
              _this.treeData = {
                nodes: [],
                edges: []
              };
              // 所有POP设备
              let pops = [];
              // 所有CPE设备
              let cpes = [];
              // 连线关系
              let lines = [];
              for (let i = 0; i < resultBody.length; i++) {
                for (let j = 0; j < resultBody[i].linkList.length; j++) {
                  if (resultBody[i].linkList[j].startNeInfo.neLayer === 'Mixed') {
                    pops.push({
                      x: 200,
                      y: (200 + (150 * i)),
                      size: 50,
                      type: 'image',
                      img: '/static/images/pop.png',
                      id: resultBody[i].linkList[j].startNeInfo.id.toString(),
                      label: resultBody[i].linkList[j].startNeInfo.userlabel,
                      info: resultBody[i].linkList[j].startNeInfo
                    });
                  } else {
                    cpes.push({
                      x: 600,
                      y: (100 + (100 * j)),
                      size: 50,
                      type: 'image',
                      img: '/static/images/cpe.png',
                      id: resultBody[i].linkList[j].startNeInfo.id.toString(),
                      label: resultBody[i].linkList[j].startNeInfo.userlabel,
                      info: resultBody[i].linkList[j].startNeInfo
                    });
                  }
                  if (resultBody[i].linkList[j].endNeInfo.neLayer === 'Mixed') {
                    pops.push({
                      x: 200,
                      y: (200 + (150 * i)),
                      size: 50,
                      type: 'image',
                      img: '/static/images/pop.png',
                      id: resultBody[i].linkList[j].endNeInfo.id.toString(),
                      label: resultBody[i].linkList[j].endNeInfo.userlabel,
                      info: resultBody[i].linkList[j].endNeInfo
                    });
                  } else {
                    cpes.push({
                      x: 600,
                      y: (100 + (100 * j)),
                      size: 50,
                      type: 'image',
                      img: '/static/images/cpe.png',
                      id: resultBody[i].linkList[j].endNeInfo.id.toString(),
                      label: resultBody[i].linkList[j].endNeInfo.userlabel,
                      info: resultBody[i].linkList[j].endNeInfo
                    });
                  }
                  lines.push({
                    source: resultBody[i].linkList[j].startNeInfo.id.toString(),
                    target: resultBody[i].linkList[j].endNeInfo.id.toString(),
                    style: {
                      stroke: '#37bbef'
                    }
                  });
                  if (pops.length === 2) {
                    lines.push({
                      source: pops[0].id.toString(),
                      target: pops[1].id.toString(),
                      type: 'cubic-vertical',
                      color: '#873bf4',
                      style: {
                        lineDash: [2, 2]    // 虚线边
                      }
                    });
                  }
                  if (pops.length > 2) {
                    let latInd = pops.length;
                    // eslint-disable-next-line max-depth
                    for (let k = 0; k < pops.length - 1; k++) {
                      lines.push({
                        source: pops[k].id.toString(),
                        target: pops[k + 1].id.toString(),
                        type: 'cubic-vertical',
                        color: '#873bf4',
                        style: {
                          lineDash: [2, 2]    // 虚线边
                        }
                      });
                    }
                    // 补充最后一条连线
                    lines.push({
                      source: pops[latInd - 1].id.toString(),
                      target: pops[0].id,
                      type: 'cubic-vertical',
                      color: '#873bf4',
                      style: {
                        lineDash: [2, 2]    // 虚线边
                      }
                    });
                  }
                }
              }
              // POP - CPE
              _this.treeData.nodes = pops.length > 0 ? _this.uniqueArr(pops).concat(_this.uniqueArr(cpes)) : _this.uniqueArr(cpes);
              _this.treeData.edges = lines;
              if (_this.devicetopologyChart) {
                _this.devicetopologyChart.destroy();
              }
              _this.showChart(_this.treeData);
            }
          }
        });
      }
    },
    // 画图
    showChart (data) {
      const contextMenu = new G6.Menu({
        getContent (evt) {
          return `<ul><li>》监控信息</li><li>》告警信息</li></ul>`;
        },
        handleMenuClick: (target, item) => {
          let title = target.outerText;
          if (title.indexOf('监控') > -1) {
            this.neInfo(item.getModel().info.id);
          } else {
            this.alarmInfo(item.getModel().info);
          }
        },
        offsetX: 16 + 10,
        offsetY: 0,
        className: 'right-menu',
        itemTypes: ['node'],
      });
      const tooltip = new G6.Tooltip({
        // offsetX 与 offsetY 需要加上父容器的 padding
        offsetX: 20,
        offsetY: 20,
        trigger: 'click',
        // 允许出现 tooltip 的 item 类型
        itemTypes: ['node'],
        className: 'tooltip-box',
        // 自定义 tooltip 内容
        getContent: (e) => {
          const outDiv = document.createElement('div');
          outDiv.style.width = '210px';
          outDiv.innerHTML = `
            <ul>
              <li><label>ID</label><span>${e.item.getModel().info.id}</span></li>
              <li><label>名称</label><span>${e.item.getModel().info.userlabel}</span></li>
              <li><label>类型</label><span>${e.item.getModel().info.typeLabel}</span></li>
              <li><label>租户</label><span>${e.item.getModel().info.extAttrs.dgName}</span></li>
              <li><label>IP</label><span>${e.item.getModel().info.ipAddress}</span></li>
              <li><label>软件SN</label><span>${this.filterSN(e.item.getModel().info.extAttrs.esn)}</span></li>
              <li><label>状态</label><span>${this.filterStatus(e.item.getModel().info)}</span></li>
            </ul>
          `;
          return outDiv;
        }
      });
      const container = document.getElementById('container');
      if (container) {
        const width = container.clientWidth;
        const height = window.innerHeight - 200;
        const graph = new G6.Graph({
          // 使用 contextMenu plugins 时，需要将 container 设置为 position: relative;
          container: 'container',
          plugins: [tooltip, contextMenu],
          width,
          height,
          defaultNode: {
            size: [80, 40],
            type: 'rect',
            style: {
              fill: '#DEE9FF',
              stroke: '#5B8FF9'
            }
          },
          defaultEdge: {
            style: {
              stroke: '#b5b5b5',
              lineAppendWidth: 3 // Enlarge the range the edge to be hitted
            },
            labelCfg: {
              autoRotate: true,
              style: {
                // A white stroke with width 5 helps show the label more clearly avoiding the interference of the overlapped edge
                stroke: 'white',
                lineWidth: 5
              }
            }
          },
          modes: {
            default: ['drag-canvas', 'zoom-canvas', 'drag-node']
          },
          layout: {
            type: 'force',
            preventOverlap: true,
            linkDistance: 200,
            // 默认为图的中心
            center: [width / 2, height / 2],
            // 开启 web-worker
            // workerEnabled: true,
            // 开启 GPU 并行计算，G6 4.0 支持
            // gpuEnabled: true
          }
        });
        this.devicetopologyChart = graph;
        // 初始化数据
        graph.data(data);
        // 渲染视图
        graph.render();
        // 中心点
        // graph.fitCenter(true);
      }
    },
    // 网元ID
    neInfo (id) {
      localStorage.setItem('monitor', id);
      // 根据ID查询网元相关信息
      this.$router.push({
        path: `/monitor/device-info`,
        query: {
          id: id
        }
      });
    },
    // 网元告警信息
    alarmInfo (ne) {
      let _this = this;
      _this.ne = ne;
      _this.dialogAlarms = true;
      // setTimeout(() => {
      //   _this.$refs.alarms.refresh();
      // }, 100);
    },
    // 取消
    cancel () {
      let _this = this;
      _this.dialogAlarms = false;
    }
  }
};
</script>

<style>
.topology-box {
  overflow: hidden;
}
.tooltip-box {
  font-size: 14px;
  line-height: 24px;
  color: #333;
  padding: 10px;
  border-radius: 5px;
  background-color: rgba(255, 255, 255, 0.8);
  box-shadow: rgb(174 174 174) 0px 0px 10px;
}
.tooltip-box ul li {
  margin-bottom: 1px;
}
.tooltip-box ul li:last-child {
  margin-bottom: 0;
}
.tooltip-box ul li label {
  display: inline-block;
  width: 60px;
  background: #ebebeb;
  text-align: center;
}
.tooltip-box ul li span {
  text-align: left;
  color: #333;
  font-size: 14px;
  line-height: 28px;
  width: 150px;
  padding-left: 10px;
}
.right-menu {
  font-size: 14px;
  line-height: 24px;
  color: #333;
  padding: 10px;
  border-radius: 5px;
  background-color: rgba(255, 255, 255, 0.8);
  box-shadow: rgb(174 174 174) 0px 0px 10px;
}
.right-menu ul li {
  cursor: pointer;
}
.right-menu ul li:hover {
  color: #3296fa !important;
}
</style>